/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2024年6月7日
 * V4.0
 */
package com.jphenix.share.tools;

//#region 【引用区】
import java.io.File;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;

import com.jphenix.standard.docs.ClassInfo;
//#endregion

//#region 【类说明】
/**
 * 文件行读取器（支持大文件）
 * 
 * com.jphenix.share.tools.FileLineReader
 * 
 * 2024-06-07 创建
 * 2024-06-11 增加支持不同的文件编码格式
 * 
 * @author MBG
 * 2024年6月7日
 */
//#endregion
@ClassInfo({"2024-06-11 13:50","操作系统相关工具"})
public class FileLineReader {
	
	//#region 【变量声明区】
	// RandomAccessFile 默认只支持这个编码
	private final String DEFAULT_ENCODING = "ISO-8859-1";

	// 文件行位置容器
	private Map<Long,Long> linePointMap = new HashMap<>();

	// 当前读取的最大行号
	private long maxLineNo = 0;
	
	// 当前行号（从1开始）
	private long currentLineNo = 0;
	
	// 随机文件读取类
	private RandomAccessFile raf = null;
	
	// 文件全路径
	private String filePath = null;
	
	// 文件对象
	private File file = null;
	
	// 是否可以读取
	private boolean canRead = false;
	
	// 最后一行字符串
	private String lastLine = null;
	
	// 文件编码格式（默认是ISO-8859-01）
	private String encoding = null;
	//#endregion
	
	//#region 【静态】readFile(File) 准备读取指定文件并返回当前类实例
	/**
	 * 【静态方法】准备读取指定文件并返回当前类实例
	 * @param file        文件对象
	 * @return            读取文件的类实例
	 * @throws Exception  文件打开异常
	 * 2024年6月7日
	 * @author MBG
	 */
	public static FileLineReader readFile(File file) throws Exception {
		// 构建返回值
		FileLineReader flr = new FileLineReader();
		flr.setFile(file); // 设置文件路径
		return flr;
	}
	//#endregion
	
	//#region 【静态】readFile(File,fileEnc) 准备读取指定文件并返回当前类实例
	/**
	 * 【静态方法】准备读取指定文件并返回当前类实例
	 * @param file        文件对象
	 * @param fileEnc     文件编码格式（默认ISO-8859-1）
	 * @return            读取文件的类实例
	 * @throws Exception  文件打开异常
	 * 2024年6月7日
	 * @author MBG
	 */
	public static FileLineReader readFile(File file,String fileEnc) throws Exception {
		// 构建返回值
		FileLineReader flr = new FileLineReader();
		flr.setEncoding(fileEnc); // 设置文件编码格式
		flr.setFile(file);             // 设置文件路径
		return flr;
	}
	//#endregion
	
	//#region 【静态】 readFile(filePath) 准备读取指定文件并返回当前类实例
	/**
	 * 【静态方法】准备读取指定文件并返回当前类实例
	 * @param filePath     文件绝对路径
	 * @return             读取文件的类实例
	 * @throws Exception   文件打开异常
	 * 2024年6月7日
	 * @author MBG
	 */
	public static FileLineReader readFile(String filePath) throws Exception {
		// 构建返回值
		FileLineReader flr = new FileLineReader();
		flr.setFilePath(filePath); // 设置文件路径
		return flr;
	}
	//#endregion
	
	//#region 【静态】readFile(filePath,fileEnc) 准备读取指定文件并返回当前类实例
	/**
	 * 【静态方法】准备读取指定文件并返回当前类实例
	 * @param filePath     文件绝对路径
	 * @param fileEnc      文件编码格式
	 * @return             读取文件的类实例
	 * @throws Exception   文件打开异常
	 * 2024年6月7日
	 * @author MBG
	 */
	public static FileLineReader readFile(String filePath,String fileEnc) throws Exception {
		// 构建返回值
		FileLineReader flr = new FileLineReader();
		flr.setEncoding(fileEnc);  // 设置文件编码格式
		flr.setFilePath(filePath); // 设置文件路径
		return flr;
	}
	//#endregion
	
	//#region BOF() 定位到文件开始位置
	/**
	 * 定位到文件开始位置
	 * @throws Exception 读取文件发生异常
	 * 2024年6月7日
	 * @author MBG
	 */
	public void BOF() throws Exception {
		if(raf==null) {
			// 读取流已经被清空，可能是因为异常引起，需要重新打开读取
			if(file!=null) {
				setFile(file); // 重新设置文件对象，重新打开文件
			}
			if(filePath!=null && filePath.length()>0) {
				setFilePath(filePath); // 重新设置文件路径，重新打开文件 
				return;
			}
			throw new Exception("The File Object Is Empty");
		}
		// 定位到开始位置
		raf.seek(0);
		currentLineNo = 0;
		canRead       = true;
	}
	//#endregion
	
	//#region getLastLine() 获取当前读取的最后一行记录
	/**
	 * 获取当前读取的最后一行记录
	 * 注意：并不一定是文件最后一行记录
	 * @return 当前读取的最后一行记录
	 * 2024年6月7日
	 * @author MBG
	 */
	public String getLastLine() {
		if(lastLine==null) {
			return lastLine;
		}
		if(encoding!=null && encoding.length()>0 && !DEFAULT_ENCODING.equals(encoding)) {
			try {
				return new String(lastLine.getBytes(DEFAULT_ENCODING),encoding);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
		return lastLine;
	}
	//#endregion
	
	//#region canRead() 是否可以继续往下读取行
	/**
	 * 是否可以继续往下读取行
	 * @return 是否可以继续往下读取行
	 * 2024年6月7日
	 * @author MBG
	 */
	public boolean canRead() {
		return canRead;
	}
	//#endregion
	
	//#region readLine() 读取指定行
	/**
	 * 读取指定行
	 * @param lineNo 行号（从1开始）
	 * @return 行记录字符串
	 * 2024年6月7日
	 * @author MBG
	 */
	public String readLine(long lineNo) {
		if(raf==null || lineNo<1) {
			return null;
		}
		if(lineNo<currentLineNo) {
			canRead = true;
			try {
				if(lineNo==1) {
					raf.seek(0);
				}else {
					raf.seek(linePointMap.get(lineNo));
				}
			}catch(Exception e) {
				e.printStackTrace();
				try {
					raf.close();
				}catch(Exception e2) {}
				raf = null;
				return null;
			}
			currentLineNo = lineNo;
			return readLine();
		}
		String line = null; // 读取的行字符串
		while(lineNo>currentLineNo && canRead) {
			line = readLine();
		}
		return line;
	}
	//#endregion
	
	//#region readLine() 读取一行
	/**
	 * 读取一行
	 * @return 读取的行字符串
	 * 2024年6月7日
	 * @author MBG
	 */
	public String readLine() {
		String line = null;
		try {
			line = raf.readLine();
		}catch(Exception e) {
			e.printStackTrace();
			try {
				raf.close();
			}catch(Exception e2) {}
			raf = null;
		}
		if(line==null) {
			canRead = false;
		} else {
			currentLineNo++;
			if(currentLineNo>maxLineNo) {
				maxLineNo = currentLineNo;
				lastLine  = line;
				try {
					// 保存当前位置，即下一行的起始位置
					linePointMap.put(currentLineNo+1,raf.getFilePointer());
				}catch(Exception e) {
					e.printStackTrace();
					try {
						raf.close();
					}catch(Exception e2) {}
					raf = null;
				}
			}
		}
		if(line==null) {
			return line;
		}
		if(encoding!=null && encoding.length()>0 && !DEFAULT_ENCODING.equals(encoding)) {
			try {
				return new String(line.getBytes(DEFAULT_ENCODING),encoding);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
		return line;
	}
	//#endregion
	
	//#region getMaxLineNo() 返回当前已经读取的最大行号
	/**
	 * 返回当前已经读取的最大行号
	 * 注意：并不一定是文件最大行号
	 * @return 当前已经读取的最大行号
	 * 2024年6月7日
	 * @author MBG
	 */
	public long getMaxLineNo() {
		return maxLineNo;
	}
	//#endregion
	
	//#region getCurrentLineNo() 返回当前行号（从1开始）
	/**
	 * 返回当前行号（从1开始）
	 * @return 当前行号
	 * 2024年6月7日
	 * @author MBG
	 */
	public long getCurrentLineNo() {
		return currentLineNo;
	}
	//#endregion
	
	//#region setFile(File) 设置需要处理的文件对象
	/**
	 * 设置需要处理的文件对象
	 * @param file       需要处理的文件对象
	 * @throws Exception 打开文件异常
	 * 2024年6月7日
	 * @author MBG
	 */
	public void setFile(File file) throws Exception {
		close(); // 先释放资源
		if(file==null) {
			return;
		}
		this.file     = file;
		this.filePath = file.getPath();
		this.raf      = new RandomAccessFile(file,"r");
		canRead       = true;
	}
	//#endregion
	
	//#region getFile() 返回需要处理的文件对象
	/**
	 * 返回需要处理的文件对象
	 * @return 需要处理的文件对象
	 * 2024年6月7日
	 * @author MBG
	 */
	public File getFile() {
		return file;
	}
	//#endregion
	
	//#region setFilePath(filePath) 设置文件路径，并打开文件
	/**
	 * 设置文件路径，并打开文件
	 * @param filePath   文件绝对路径
	 * @throws Exception 打开文件发生异常
	 * 2024年6月7日
	 * @author MBG
	 */
	public void setFilePath(String filePath) throws Exception {
		close(); // 先释放资源
		this.filePath = filePath;
		file          = new File(filePath);
		raf           = new RandomAccessFile(file,"r");
		canRead       = true;
	}
	//#endregion
	
	//#region getFilePath() 返回要处理的文件路径
	/**
	 * 返回要处理的文件路径
	 * @return 要处理的文件路径
	 * 2024年6月7日
	 * @author MBG
	 */
	public String getFilePath() {
		return filePath;
	}
	//#endregion
	
	//#region setEncoding(encoding) 设置文件编码格式（默认ISO-8859-1）
	/**
	 * 设置文件编码格式（默认ISO-8859-1）
	 * @param encoding 文件内容编码格式
	 * 2024年6月11日
	 * @author MBG
	 */
	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}
	//#endregion
	
	//#region getEncoding() 返回当前文件编码格式
	/**
	 * 返回当前文件编码格式
	 * @return 当前文件编码格式
	 * 2024年6月11日
	 * @author MBG
	 */
	public String getEncoding() {
		if(encoding==null || encoding.length()<1) {
			return DEFAULT_ENCODING;
		}
		return encoding;
	}
	//#endregion
	
	//#region close() 关闭文件流释放资源
	/**
	 * 关闭文件流释放资源
	 * 2024年6月7日
	 * @author MBG
	 */
	public void close() {
		canRead       = false;
		currentLineNo = 0;
		filePath      = null;
		file          = null;
		lastLine      = null;
		linePointMap.clear();
		if(raf!=null) {
			try {
				raf.close();
			}catch(Exception e) {}
		}
	}
	//#endregion
}











